#ifndef __M_SESSION_H__
#define __M_SESSION_H__

#include "util.hpp"
#include <unordered_map>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>

typedef enum
{
    UNLOGIN,
    LOGIN
} ss_status;

class session
{
public:
    session(uint64_t ssid)
    :_ssid(ssid), _status(UNLOGIN)
    {
        DLOG("session %p 被创建！", this);
    }
    uint64_t ssid() { return _ssid; }
    void set_user(uint64_t uid) { _uid = uid; }
    void set_status(ss_status status) { _status = status; }
    uint64_t get_user() { return _uid; }
    bool is_login() { return _status == LOGIN; }
    void set_timer(const wsserver_t::timer_ptr& tp) { _tp = tp; }
    wsserver_t::timer_ptr& get_timer() { return _tp; }
    ~session()
    {
        DLOG("session %p 被释放！", this);
    }
private:
    uint64_t _ssid; // 标识符
    uint64_t _uid; // session对应的用户id
    ss_status _status; // 用户状态
    wsserver_t::timer_ptr _tp; // session关联的定时器
};

#define SESSION_TIMEOUT 30000
#define SESSION_FOREVER -1
using session_ptr = std::shared_ptr<session>;

class session_manager
{
public:
    session_manager(wsserver_t* srv)
    :_next_ssid(1), _server(srv)
    {
        DLOG("session管理器初始化完毕！");
    }
    session_ptr create_session(uint64_t uid, ss_status stauts)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        session_ptr ssp = std::make_shared<session>(_next_ssid);
        ssp->set_status(stauts);
        ssp->set_user(uid);
        _session.insert(std::make_pair(_next_ssid, ssp));
        _next_ssid++;
        return ssp;
    }
    void append_session(const session_ptr& ssp)
    { 
        std::unique_lock<std::mutex> lock(_mutex);
        _session.insert(std::make_pair(ssp->ssid(), ssp));
    }
    session_ptr get_session_by_ssid(uint64_t ssid)
    {
        auto it = _session.find(ssid);
        if (it == _session.end())
        {
            return session_ptr();
        }
        return it->second;
    }
    void remove_session(uint64_t ssid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _session.erase(ssid);
    }
    void set_session_expire_time(uint64_t ssid, int ms) // 设置会话指定时间失效
    {   
        // 在游戏大厅或房间则session永久存在，否则设置为临时，长时间无通信后退出
        session_ptr ssp = get_session_by_ssid(ssid);
        if (ssp.get() == nullptr)
            return;
        wsserver_t::timer_ptr tp = ssp->get_timer();
        if (tp.get() == nullptr && ms == SESSION_FOREVER)
        {
            // tp.get()为空说明没有设置定时删除任务，永久存在
            // session永久存在，设置永久存在
            return;
        }
        else if (tp.get() == nullptr && ms != SESSION_FOREVER)
        {
            // session永久存在，设置定时任务
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms, std::bind(&session_manager::remove_session, this, ssid));
            ssp->set_timer(tmp_tp);
        }
        else if (tp.get() != nullptr && ms == SESSION_FOREVER)
        {
            // session存在定时任务，设置session永久存在
            // cancel删除定时任务，此定时任务函数会导致该定时器设置的任务直接被执行
            tp->cancel();
            ssp->set_timer(wsserver_t::timer_ptr()); // 将session关联的定时器设置为空
            // 所以需要重新给session管理器添加一个session信息
            // 由于cancel并不是立即取消的，顺序可能会倒置，所以需要特殊处理
            _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));
            //_session.insert(std::make_pair(ssp->ssid(), ssp));
        }
        else if (tp.get() != nullptr && ms != SESSION_FOREVER)
        {
            // session存在定时任务，重置session删除时间
            tp->cancel();
            _server->set_timer(0, std::bind(&session_manager::append_session, this, ssp));
            ssp->set_timer(wsserver_t::timer_ptr());

            // 重新给session添加定时销毁任务
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms, std::bind(&session_manager::remove_session, this, ssid));
            ssp->set_timer(tmp_tp);
        }
    }
    ~session_manager()
    {
        DLOG("session管理器正在销毁！");
    }
private:
    uint64_t _next_ssid;
    std::mutex _mutex;
    std::unordered_map<uint64_t, session_ptr> _session; // 会话id和信息指针建立映射关系
    wsserver_t* _server; // 添加定时任务
};

#endif